<template>
  <view class="verifyCode">
    <view class="verifyCode-title">
      <text class="verifyCode-title-text">请完成安全验证</text>
      <wd-icon name="close-outline" color="#9CA4AC" size="18px" @click="handleClose"></wd-icon>
    </view>
    <view v-if="!loading" class="verifyCode-view">
      <view
        class="verifyCode-view-panel"
        :style="{ backgroundImage: randomBackGround }"
        @click="handlePanelClick"
      >
        <view
          v-for="(item, index) in mosPosArray"
          :key="index"
          class="selected"
          :style="{ left: `${item.x}px`, top: `${item.y}px` }"
          @click.stop="cancelSelectedPoint(item, index + 1)"
        >
          <text class="selected-text">{{ index + 1 }}</text>
        </view>
        <text
          v-for="(item, index) in posArray"
          :key="index"
          class="originalText"
          :style="{
            transform: `rotate(${item.rotate}deg)`,
            left: `${item.text_x}px`,
            top: `${item.text_y}px`,
            fontSize: `${item.fontSize}rpx`,
            fontWeight: `${item.fontWeight}`,
            color: `${item.color}`,
          }"
          @click="handleTextClick(item)"
        >
          {{ item.word }}
        </text>
        <view v-if="!pass" class="refresh" @click.stop="refresh">
          <wd-icon name="refresh" color="#FFFFFF" size="44rpx"></wd-icon>
        </view>
      </view>
      <view v-if="!verifying" class="verifyCode-view-tips">
        <text class="verifyCode-view-tips-text">
          请依次点击
          <text v-for="(item, index) in presetsTextArray" :key="index" class="text-weight">
            "{{ item }}"
          </text>
        </text>
      </view>
      <view
        v-else
        class="verifyCode-view-tips"
        :style="{
          backgroundColor: pass ? '#d2f4ef' : '#fce1e1',
          borderColor: pass ? '#52ccba' : '#f57a7a',
        }"
      >
        <wd-icon v-if="pass" name="check" size="34rpx" color="#20C8AE"></wd-icon>
        <wd-icon v-else name="close" size="34rpx" color="#F06767"></wd-icon>
        <text
          class="verifyCode-view-tips-text"
          :style="{ color: pass ? '#52ccba' : '#f57a7a', paddingLeft: '6rpx' }"
        >
          {{ pass ? '验证成功' : '验证失败，请重试' }}
        </text>
      </view>
    </view>
    <view v-else class="loading">
      <view class="loading-panel">
        <image class="loading-panel-image" src="/static/loading.gif" mode="aspectFit"></image>
      </view>
      <view class="loading-tips">
        <text>加载中...</text>
      </view>
    </view>
  </view>
</template>

<script setup>
// 【修改点】: 移除了 nextTick，因为不再需要等待弹窗动画
import { ref, onMounted, getCurrentInstance } from 'vue'

const codeCharacter = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F'] // 16进制数
const posArray = ref([]) // 随机文字集合
const mosPosArray = ref([]) // 点击的坐标集合
const randomBackGround = ref('') // 随机图片背景
const startLeft = ref(0) // 可点击的范围距离左侧边界的距离
const startTop = ref(0) // 可点击的范围距离上侧边界的距离
const clickInt = ref(0) // 点击次数
const selectedArray = ref([]) // 用户选中文字
const presetsTextArray = ref([]) // 预设文字集合
const loading = ref(false) // 是否正在加载中
const verifying = ref(false) // 是否在验证中
const pass = ref(false) // 验证是否通过

// 【修改点】: 不再需要 alertPopup 的 ref
const instance = getCurrentInstance()

// 【修改点】: emit 事件增加了 'close'
const emit = defineEmits(['success', 'close'])

// 【修改点】: 新增的关闭处理函数
const handleClose = () => {
  emit('close') // 通知父组件用户想关闭
}

const refresh = () => {
  init()
}

const cancelSelectedPoint = (item, index) => {
  console.log('字体天机', item, index)
  if (index === clickInt.value && clickInt.value < 3) {
    mosPosArray.value = index == 1 ? [] : mosPosArray.value.splice(item, 1)
    clickInt.value = index == 1 ? 0 : clickInt.value - 1
    selectedArray.value =
      index == 1
        ? []
        : selectedArray.value.length > 1
          ? selectedArray.value.slice(0, 1)
          : selectedArray.value
  }
}

const handlePanelClick = (e) => {
  console.log(e, '点击')
  if (clickInt.value < 3) {
    mosPosArray.value.push({
      x: e.detail.x - startLeft.value,
      y: e.detail.y - startTop.value,
    })
    clickInt.value++
  }
  compareText()
}

const handleTextClick = (textItem) => {
  if (clickInt.value < 3) {
    selectedArray.value.push(textItem.word)
  }
  console.log(textItem, '点击文字', selectedArray.value)
}

const compareText = () => {
  if (clickInt.value === 3) {
    verifying.value = true
    if (presetsTextArray.value.toString() === selectedArray.value.toString()) {
      pass.value = true
      emit('success', true) // 验证成功后通知父组件
    } else {
      pass.value = false
      setTimeout(() => {
        init()
      }, 600)
    }
  }
}

const init = async () => {
  loading.value = true
  verifying.value = false
  pass.value = false
  mosPosArray.value = []
  selectedArray.value = []
  clickInt.value = 0

  getRandomBackGround()

  const posInfo = await getPosition()
  if (!posInfo) {
    loading.value = false
    console.error('无法获取验证码面板尺寸，初始化失败。')
    return
  }
  startLeft.value = posInfo.left
  startTop.value = posInfo.top

  const tempPosArray = []
  for (let i = 0; i < 4; i++) {
    tempPosArray.push({
      index: i,
      word: getRandomText(),
      color: getRandomColor(),
      text_x: getRandomPosition(i, posInfo.width, posInfo.height, 'x'),
      text_y: getRandomPosition(i, posInfo.width, posInfo.height, 'y'),
      rotate: getRandomRotate(),
      fontSize: getRandomSize(),
      fontWeight: getRandomWeight(),
    })
  }
  posArray.value = tempPosArray

  setTimeout(() => {
    getRandomPresetsCode()
    loading.value = false
  }, 600)
}

const getPosition = () => {
  return new Promise((resolve) => {
    const query = uni.createSelectorQuery().in(instance)
    query
      .select('.verifyCode-view-panel')
      .boundingClientRect((data) => {
        resolve(data)
      })
      .exec()
  })
}

// 【修改点】: 组件挂载时自动初始化
onMounted(() => {
  init()
})

// 生成随机背景图片
const getRandomBackGround = () => {
  randomBackGround.value = `url(/static/${Math.floor(Math.random() * 9) + 1}.png)`
}
// 在随机的文字中挑选3个作为预设验证码
const getRandomPresetsCode = () => {
  const a = [...posArray.value]
  const b = []
  for (let i = 0; i < 3; i++) {
    b.push(a.splice(Math.floor(Math.random() * a.length), 1)[0].word)
  }
  presetsTextArray.value = b
}
// 生成随机文字
const getRandomText = () => {
  let initStr = '0x'
  const tempStr3 = 4 + Math.round(Math.random() * 5)
  initStr += codeCharacter[tempStr3]
  const tempStr4 = tempStr3 === 4 ? 14 + Math.round(Math.random()) : Math.round(Math.random() * 15)
  initStr += codeCharacter[tempStr4]
  const tempStr5 =
    tempStr4 === 15 && tempStr3 === 9
      ? Math.round(Math.random() * 10)
      : Math.round(Math.random() * 15)
  initStr += codeCharacter[tempStr5]
  const tempStr6 =
    tempStr3 === 9 && tempStr4 === 15 && tempStr5 === 10
      ? Math.round(Math.random() * 5)
      : Math.round(Math.random() * 15)
  initStr += codeCharacter[tempStr6]
  return String.fromCharCode(initStr)
}
// 生成随机坐标
const getRandomPosition = (int, width, height, position) => {
  const maxTop =
    (int < 2 ? height / 2 : height) - 30 > 0
      ? (int < 2 ? height / 2 : height) - 30
      : int < 2
        ? height / 2
        : height
  const minTop = int < 2 ? 0 : height / 2
  const maxLeft =
    (int % 2 === 0 ? width / 2 : width) - 30 > 0
      ? (int % 2 === 0 ? width / 2 : width) - 30
      : int % 2 === 0
        ? width / 2
        : width
  const minLeft = int % 2 === 0 ? 0 : width / 2
  return position == 'x'
    ? Math.floor(Math.random() * (maxLeft - minLeft)) + minLeft
    : Math.floor(Math.random() * (maxTop - minTop)) + minTop
}
// 生成随机颜色
const getRandomColor = () => {
  const letters = '0123456789ABCDEF'
  let color = '#'
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)]
  }
  return color
}
// 设置文字随机字体大小 在40和52之间，单位px
const getRandomSize = () => Math.floor(Math.random() * (52 - 40)) + 40
// 设置文字随机粗体 在500和1000之间
const getRandomWeight = () => Math.floor(Math.random() * (1000 - 500)) + 500
// 生成文字随机旋转角度
const getRandomRotate = () =>
  Math.round(Math.random()) == 1 ? Math.floor(Math.random() * 45) : -Math.floor(Math.random() * 45)

// 【修改点】: 现在暴露给父组件的是 refresh 方法
defineExpose({
  refresh,
})
</script>
<script lang="ts">
export default {
  options: {
    virtualHost: true,
  },
}
</script>
<style lang="scss" scoped>
.verifyCode {
  background-color: #ffffff;
  border-radius: 2px;

  &-title {
    display: flex;
    justify-content: space-between;
    padding: 30rpx;
    border-bottom: 1px solid #e4e7eb;

    &-text {
      font-size: 32rpx;
      color: #45494c;
    }
  }

  &-view {
    padding: 30rpx;

    &-panel {
      position: relative;
      width: 100%;
      height: 320rpx;
      margin-bottom: 30rpx;
      cursor: pointer;
      background-repeat: no-repeat;
      background-size: cover;

      .selected {
        position: absolute;
        z-index: 98;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 50rpx;
        height: 50rpx;
        background-color: #76a1d3;
        border-radius: 50%;
        opacity: 0.8;
        transform: translate(-50%, -50%);

        &-text {
          position: absolute;
          top: 44%;
          left: 50%;
          z-index: 9;
          font-size: 24rpx;
          color: #ffffff;
          transform: translate(-50%, -50%);
        }
      }

      .originalText {
        position: absolute;
      }

      .refresh {
        position: absolute;
        top: 0;
        right: 0;
        z-index: 99;
        display: inline-block;
        padding: 12rpx;
        background-color: rgba(0, 0, 0, 0.12);
      }
    }

    &-tips {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 76rpx;
      background-color: #f7f9fa;
      border: 1px solid #e4e7eb;
      border-radius: 2px;

      &-text {
        font-size: 28rpx;
      }

      .text-weight {
        margin-left: 8rpx;
        font-weight: 700;
      }
    }
  }

  .loading {
    padding: 30rpx;

    &-panel {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 320rpx;
      margin-bottom: 30rpx;
      background-color: #fbf7f3;

      &-image {
        width: 300rpx;
        height: 300rpx;
      }
    }

    &-tips {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 76rpx;
      font-size: 28rpx;
      background-color: #f7f9fa;
      border: 1px solid #e4e7eb;
      border-radius: 2px;
    }
  }
}
</style>
L
